package ch.ethz.fcl.metrobuzz.render.accessbility.bus_schedule;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Paint;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.util.List;

import javax.swing.JFrame;

import org.apache.commons.collections15.Transformer;

import ch.ethz.fcl.metrobuzz.data.transit.TransitRoute;
import ch.ethz.fcl.metrobuzz.tools.MBTools;

import edu.uci.ics.jung.algorithms.layout.StaticLayout;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.SparseMultigraph;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
import edu.uci.ics.jung.visualization.decorators.EdgeShape;

/**
 * Use JUNG to visualize bus schedules passing by. each bus stop each individual
 * line indicates one bus routes
 * 
 * x-axis: time y-axis: stop
 * 
 * @author Zeng Wei
 * @email zeng@arch.ethz.ch
 * 
 */
public class BusScheduleVisualizer {
	private int width = 800, height = 800;
	private int xAxisRatio = 10;
	private TransitRoute route;
	private List<List<Float>> busesSchedules;
	private int stopsSize;

	public BusScheduleVisualizer(TransitRoute route, List<List<Float>> schedule) {
		this.route = route;
		this.busesSchedules = schedule;
		stopsSize = schedule.get(0).size();
	}

	public void render() {
		Graph<String, String> basis = createGraph();

		StaticLayout<String, String> layout = new StaticLayout<String, String>(
				basis, vertexLocationTransformer());
		layout.setSize(new Dimension(width, height));
		VisualizationViewer<String, String> vv = new VisualizationViewer<String, String>(
				layout);

		vv.getRenderContext().setVertexShapeTransformer(
				vertexShapeTransformer());
		vv.getRenderContext().setVertexLabelTransformer(
				vertexLabelTransformer());
		vv.getRenderContext().setEdgeDrawPaintTransformer(
				edgeColorTransformer());

		// set to straight line
		vv.getRenderContext().setEdgeShapeTransformer(
				new EdgeShape.Line<String, String>());

		vv.setPreferredSize(new Dimension(width, height));
		vv.setGraphMouse(new DefaultModalGraphMouse<Number, Number>());

		JFrame frame = new JFrame("Bus " + route.getId() + " schedule");
		frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		frame.getContentPane().add(vv);
		vv.setOpaque(false);
		frame.pack();
		frame.setVisible(true);
	}

	private Graph<String, String> createGraph() {
		Graph<String, String> graph = new SparseMultigraph<String, String>();

		for (int k = 0; k < busesSchedules.size(); k++) {
			for (int i = 0; i < stopsSize; i++) {
				graph.addVertex(new Integer(k * stopsSize + i).toString());
				if (i > 0)
					graph.addEdge(
							"Bus index " + k + " stop index "
									+ Integer.valueOf(i - 1) + ":"
									+ Integer.valueOf(i),
							Integer.valueOf(k * stopsSize + i - 1).toString(),
							Integer.valueOf(k * stopsSize + i).toString());
			}
		}

		for (int i = 0; i <= 24; i++) {
			graph.addVertex("Time:" + i);
			if (i > 0)
				graph.addEdge("Time: " + (i - 1) + " " + i, "Time:" + (i - 1),
						"Time:" + i);
		}

		for (int i = 0; i < stopsSize; i++) {
			graph.addVertex("StopIndex:" + i);
			if (i > 0) {
				graph.addEdge("Edge:" + (i - 1) + " " + i, "Stop:" + (i - 1),
						"StopIndex:" + i);
			}
		}

		return graph;
	}

	private Transformer<String, Point2D> vertexLocationTransformer() {
		return new Transformer<String, Point2D>() {
			@Override
			public Point2D transform(String vertexName) {
				if (vertexName.startsWith("Time")) {
					String[] st = vertexName.split(":");
					int hour = Integer.parseInt(st[1]);
					return new Point2D.Float(width * xAxisRatio * hour / 24,
							height);
				} else if (vertexName.startsWith("Stop")) {
					String[] st = vertexName.split(":");
					int stopIndex = Integer.parseInt(st[1]);
					float originStopDist = route.getStopDist(route.getStops()
							.get(0), route.getStops().get(stopIndex));
					float y = height - height / route.getRouteDist()
							* originStopDist;
					if (stopIndex == 0)
						y = height;
					return new Point2D.Float(0, y);
				} else {
					int vertex = Integer.parseInt(vertexName);
					int busIndex = vertex / stopsSize;
					int stopIndex = vertex % stopsSize;
					float x = MBTools.map(
							busesSchedules.get(busIndex).get(stopIndex), 0,
							3600 * 24, 0, width * xAxisRatio);
					float originStopDist = route.getStopDist(route.getStops()
							.get(0), route.getStops().get(stopIndex));
					float y = height - height / route.getRouteDist()
							* originStopDist;
					if (stopIndex == 0)
						y = height;

					return new Point2D.Double(x, y);
				}
			}
		};
	}

	private Transformer<String, Shape> vertexShapeTransformer() {
		return new Transformer<String, Shape>() {
			public Shape transform(String i) {
				if (i.startsWith("Time"))
					return new Ellipse2D.Double(-2, -2, 4, 4);
				Ellipse2D circle = new Ellipse2D.Double(-1, -1, 2, 2);
				return circle;
			}
		};
	}

	private Transformer<String, String> vertexLabelTransformer() {
		return new Transformer<String, String>() {
			@Override
			public String transform(String vertex) {
				if (vertex.startsWith("Time")) {
					String[] st = vertex.split(":");
					return st[1];
				}

				else if (vertex.startsWith("Stop")) {
					String[] st = vertex.split(":");
					int stopIndex = Integer.parseInt(st[1]);
					return route.getStops().get(stopIndex).getId();
				}
				return "";
			}
		};
	}

	private Transformer<String, Paint> edgeColorTransformer() {
		return new Transformer<String, Paint>() {
			public Paint transform(String s) {
				if (s.startsWith("Time") || s.startsWith("Edge"))
					return Color.GRAY;

				String[] sts = s.split(" ");
				int busIndex = Integer.parseInt(sts[2]);

				switch (busIndex % 7) {
				case 0:
					return Color.red;

				case 1:
					return Color.GREEN;

				case 2:
					return Color.blue;

				case 3:
					return Color.CYAN;

				case 4:
					return Color.PINK;

				case 5:
					return Color.GRAY;

				case 6:
					return Color.YELLOW;

				default:
					return Color.gray;
				}
			}
		};
	}

}
